#include <bits/stdc++.h>
using namespace std;

class UnionFind {
private:
    vector<int> parent, rankv;
public:
    UnionFind(int n) : parent(n), rankv(n, 0){
        for(int i = 0; i < n; i++){
            parent[i] = i;
        }
    }
    int findp(int x){
        if(parent[x] != x) parent[x] = findp(parent[x]);
        return parent[x];
    }
    void uni(int x, int y){
        x = findp(x); 
        y = findp(y);
        if(x == y) return;
        if(rankv[x] < rankv[y]) swap(x, y);
        parent[y] = x;
        if(rankv[x] == rankv[y]) rankv[x]++;
    }
};

int intersectSorted(const vector<int> &a, const vector<int> &b) {
    int i = 0, j = 0, cnt = 0;
    while(i < (int)a.size() && j < (int)b.size()){
        if(a[i] == b[j]){
            cnt++;
            i++; 
            j++;
        } else if(a[i] < b[j]){
            i++;
        } else {
            j++;
        }
    }
    return cnt;
}

class Solution {
public:
    int numberOfComponents(vector<vector<int>> &properties, int k) {
        int n = properties.size();
        for(auto &row : properties){
            sort(row.begin(), row.end());
            row.erase(unique(row.begin(), row.end()), row.end());
        }

        UnionFind uf(n);
        for(int i = 0; i < n; i++){
            for(int j = i + 1; j < n; j++){
                if((int)properties[i].size() < k || (int)properties[j].size() < k) continue;
                if(intersectSorted(properties[i], properties[j]) >= k){
                    uf.uni(i, j);
                }
            }
        }
        unordered_set<int> st;
        for(int i = 0; i < n; i++){
            st.insert(uf.findp(i));
        }
        return (int)st.size();
    }
};

int main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    // 示例：使用方式自行填写
    return 0;
}